package com.yfree.utils;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.text.TextUtils;

import com.yfree.YConfig;
import com.yfree.activities.YActivity;
import com.yfree.activities.YActivityManager;
import com.yfree.activities.YCrashActivity;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class YCrashHandler implements UncaughtExceptionHandler {
    private static YActivity baseActivity;

    public static void init(YActivity baseActivity) {
        if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof YCrashHandler)) {
            YCrashHandler.baseActivity = baseActivity;
            Thread.setDefaultUncaughtExceptionHandler(new YCrashHandler());
        }
    }

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        try {
            String msg = getExceptionMSG(baseActivity, ex);
            baseActivity.yLog(msg);
            YLogUtils.out(baseActivity, msg);
            baseActivity.context.startActivity(new Intent(baseActivity.context, YCrashActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    .putExtra(YConfig.YBundleKey.yCrashMSG, msg)
                    .putExtra(YConfig.YBundleKey.yCrashIP, YConfig.IP)
                    .putExtra(YConfig.YBundleKey.yCrashJKID, YConfig.commitErrorLog)
                    .putExtra(YConfig.YBundleKey.yCrashAlertMSG, YConfig.crashMsg)
                    .putExtra(YConfig.YBundleKey.yCrashRestartActivity, YConfig.restartActivity)
            );
        } catch (Exception ignore) {
            baseActivity.yToast(YConfig.crashMsg);
        }
        YActivityManager.finishApplication();
    }

    private String getExceptionMSG(final YActivity baseActivity, Throwable e) {
        StringBuilder sb = new StringBuilder();
        try {
            sb.append("异常时间：");
            sb.append(new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss", Locale.CHINESE).format(new Date()));

            sb.append("\n设备：");
            sb.append(Build.MANUFACTURER).append("_").append(Build.MODEL);

            try {
                sb.append("\n设备识别码：");
                String imei = YDeviceUtils.getImei(baseActivity.context);
                sb.append(TextUtils.isEmpty(imei) ? "" : imei).append("_").append(YDeviceUtils.getUniqueId(baseActivity));
            } catch (Exception ignored) {
            }

            sb.append("\napk版本号：");
            sb.append(YVersionUtils.getVersionCode(baseActivity.context)).append("_").append(YVersionUtils.getVersionName(baseActivity.context));

            if (e != null) {
                try {
                    sb.append("\n异常类型：");
                    sb.append(e.toString());
                    StackTraceElement[] stackTraceElements = e.getStackTrace();
                    for (int i = 0; i < stackTraceElements.length; i++) {
                        try {
                            StackTraceElement stackTraceElement = stackTraceElements[i];
                            sb.append("\n—————[").append(i + 1).append("]—————");
                            sb.append("\n异常文件名：");
                            sb.append(stackTraceElement.getFileName());
                            sb.append("\n异常类名：");
                            sb.append(stackTraceElement.getClassName());
                            sb.append("\n异常函数名：");
                            sb.append(stackTraceElement.getMethodName());
                            sb.append("\n异常行号：");
                            sb.append(stackTraceElement.getLineNumber());
                            sb.append("\n异常信息：");
                            sb.append(stackTraceElement.toString());
                        } catch (Exception ignored) {
                        }
                    }
                } catch (Exception ignored) {
                }
                try {
                    sb.append("\n—————[Detail]—————\n");
                    HashMap<String, String> sys = systemInfo(baseActivity.context);
                    for (Map.Entry<String, String> entry : sys.entrySet()) {
                        try {
                            sb.append(entry.getKey()).append("=").append(entry.getValue()).append("\n");
                        } catch (Exception ignored) {
                        }
                    }
                } catch (Exception ignored) {
                }
                sb.append(stackTrace(e));
            } else {
                sb.append("\n异常：null");
            }
        } catch (Exception ignored) {
        }
        return sb.toString();
    }

    private HashMap<String, String> systemInfo(Context context) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINESE);
        HashMap<String, String> map = new HashMap<>();
        try {
            PackageInfo mPackageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
            map.put("versionName", mPackageInfo.versionName);
            map.put("versionCode", "" + mPackageInfo.versionCode);
        } catch (Exception ignored) {
        }
        map.put("MODEL", "" + Build.MODEL);
        map.put("SDK_INT", "" + Build.VERSION.SDK_INT);
        map.put("PRODUCT", "" + Build.PRODUCT);
        map.put("createTime", dateFormat.format(new Date()));
        return map;
    }

    private String stackTrace(Throwable throwable) {
        StringWriter stringWriter = new StringWriter();
        PrintWriter writer = new PrintWriter(stringWriter, true);
        throwable.printStackTrace(writer);
        String result = stringWriter.toString();
        try {
            writer.flush();
            writer.close();
            stringWriter.flush();
            stringWriter.close();
        } catch (IOException ignored) {
        }
        return result;
    }
}